﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

//For now it only supports keyboard and Mouse, will add controller support later on
namespace Imagine
{
    public enum eMouseButtons
    {
        eLeftButton,
        eRightButton,
        eScrollButton,
        eXButton1,
        eXButton2
    }

    public class TheInputManager
    {
        #region Private_Parts
        KeyboardState m_currentKeyState;
        KeyboardState m_oldKeyState;

        MouseState m_currentMouseState;
        MouseState m_oldMouseState;

        List<IKeyboardListener> m_keyboardListeners;        //Input Observers waiting for some input 
        List<IMouseListener>    m_mouseListeners;           //Input Observers waiting for some input 

        void GetDownButtons(MouseState state, out List<eMouseButtons> downButtons)
        {
            downButtons = new List<eMouseButtons>();

            //Just check for each button explicitly
            if(state.LeftButton == ButtonState.Pressed)
                downButtons.Add(eMouseButtons.eLeftButton);
            
            if(state.RightButton == ButtonState.Pressed)
                downButtons.Add(eMouseButtons.eRightButton);
            
            if(state.MiddleButton == ButtonState.Pressed)
                downButtons.Add(eMouseButtons.eScrollButton);
            
            if(state.XButton1 == ButtonState.Pressed)
                downButtons.Add(eMouseButtons.eXButton1);
            
            if(state.XButton2 == ButtonState.Pressed)
                downButtons.Add(eMouseButtons.eXButton2);

        }
        #endregion

        static public TheInputManager Instance()
        {
            if(instance == null)   
                instance = new TheInputManager();
            return instance;
        }

        #region Private_constructor
        private TheInputManager()
        {
            m_currentKeyState = new KeyboardState(); //For 1 keyborad only right now
            m_oldKeyState     = new KeyboardState(); //Previous state of keyboard

            m_currentMouseState = new MouseState();
            m_oldMouseState = new MouseState();

            m_keyboardListeners = new List<IKeyboardListener>();
            m_mouseListeners = new List<IMouseListener>();
        }
        static TheInputManager instance;
        #endregion

        ///<HOW_THE_HELL_THIS_WORKS>
        ///This piece of code will check if any of the Listeners need to be notified of any event
        ///UpKeys{Up}       = PreviousState.DownKeys{ctrl,shift,UP} - CurrentState.DownKeys{ctrl,shift,LEFT}
        ///DownKeys{LEFT}   = CurrentState.DonwKeys{ctrl,shift,LEFT} - PreviousState.DownKeys{ctrl,shift,UP}  
        ///Such a simple Logic :)
        ///Happy i thought of it
        ///</HOW_THE_HELL_THIS_WORKS>
        public void Update()
        {
            //Read HOW_THE_HELL_THIS_WORKS if difficlut understanding
            m_currentKeyState = Keyboard.GetState();
            m_currentMouseState = Mouse.GetState();

            #region Keyborad
            if(m_currentKeyState != m_oldKeyState)
            {
                
                Keys[] oldDownKeys      = m_oldKeyState.GetPressedKeys();
                Keys[] currentDownKeys  = m_currentKeyState.GetPressedKeys();

                Keys[] upkeys = oldDownKeys.Except<Keys>(currentDownKeys).ToArray();
                Keys[] downkeys = currentDownKeys.Except<Keys>(oldDownKeys).ToArray();                
                

                if(upkeys.Length > 0)
                    foreach(IKeyboardListener listener in m_keyboardListeners)
                        listener.KeyUp( upkeys );

                if(currentDownKeys.Length > 0)
                    foreach(IKeyboardListener listener in m_keyboardListeners)
                        listener.KeyDown( downkeys );
            }
            #endregion

            #region Mouse
            if(m_currentMouseState != m_oldMouseState)
            {
                Vector2 oldPos = new Vector2(m_oldMouseState.X, m_oldMouseState.Y);
                Vector2 newPos = new Vector2(m_currentMouseState.X, m_currentMouseState.Y);

                if(oldPos != newPos)
                {
                     foreach(IMouseListener listener in m_mouseListeners)
                        listener.MouseMove( newPos - oldPos );
                }

                List<eMouseButtons> oldDownbuttons, newDownbuttons;
                GetDownButtons(m_oldMouseState, out oldDownbuttons);
                GetDownButtons(m_currentMouseState,out newDownbuttons);

                eMouseButtons[] upButtons = oldDownbuttons.Except<eMouseButtons>(newDownbuttons).ToArray();
                eMouseButtons[] downButtons = (eMouseButtons[])newDownbuttons.Except<eMouseButtons>(oldDownbuttons).ToArray();

                if(upButtons.Length > 0)
                    foreach(IMouseListener listener in m_mouseListeners)
                        listener.MouseButtonUp( upButtons, newPos );

                if(downButtons.Length > 0)
                    foreach(IMouseListener listener in m_mouseListeners)
                        listener.MouseButtonDown( downButtons, newPos );
            }
            #endregion

            //Before Leaving assign the Old = New
            m_oldKeyState = m_currentKeyState;
            m_oldMouseState = m_currentMouseState;

        }

        //C# wont let me do implicit registeration, because we cant have contructor's for Intefaces
        //Well i know we should'nt but still man, why constarint us :|
        //Hence this ugly piece of code :(
        public void Register<T>(T listener)    
        {
           if(typeof(T) == typeof(IMouseListener))
               m_mouseListeners.Add((IMouseListener)listener);

            if(typeof(T)  == typeof(IKeyboardListener))
                m_keyboardListeners.Add((IKeyboardListener)listener);
        }

        //For simple puny Humans, who want just simple functionality
        public Vector2 GetMousePos()       { return new Vector2(m_currentMouseState.X, m_currentMouseState.Y);   }
        
        public bool IsKeyDown(Keys key) { return m_currentKeyState.IsKeyDown(key);    }
        public bool IsKeyUp(Keys key)   { return m_currentKeyState.IsKeyUp(key);      }
        public Keys[] GetPressedKeys()  { return m_currentKeyState.GetPressedKeys();  }
    }
}
